package com.spark.market.service.impl;

import com.google.common.util.concurrent.RateLimiter;
import com.spark.market.api.dto.ItemInfo;
import com.spark.market.api.dto.OrderInfo;
import com.spark.market.api.request.OrderCreateParam;
import com.spark.market.common.exception.BusinessException;
import com.spark.market.common.exception.EmBusinessError;
import com.spark.market.common.utils.AssertUtils;
import com.spark.market.dal.entity .OrderLogDo;
import com.spark.market.dal.model.OrderModel;
import com.spark.market.service.OrderCmdCoreService;
import com.spark.market.service.manager.OrderManager;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.concurrent.*;

@Service
public class OrderCmdCoreServiceImpl implements OrderCmdCoreService {
    @Resource
    private OrderManager orderManager;

    private ExecutorService executorService;

    private RateLimiter orderCreateLimiter;
    @PostConstruct
    public void init() {
        executorService = Executors.newFixedThreadPool(20);
        //设置接口的tps
        orderCreateLimiter = RateLimiter.create(300);
    }
    /**
     * 此处的设计思路是将redis减库存的操作和后续的操作不放在一个事务中。在后续的操作中如果发生异常
     * 就会对之前redis中的库存数量进行冲正，手续事务中的操作如果在单体应用中，使用本地事务，如果在
     * 微服务中使用分布式事务对起进行回滚，同样也会将异常抛出，redis的注解会检测异常并进行redis中
     * 库存的补偿。
     *
     * @param param
     * @return
     */
    @Override
    public OrderInfo createOrder(OrderCreateParam param) {
        if (!orderCreateLimiter.tryAcquire()){ //接口限流
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
        }
        AssertUtils.isNotNull(param, "OrderCreateResult is null");
        //秒杀情况下进行令牌的校验
        if (param.getPromoId() != null) {
            orderManager.verify(param);
        }
        //校验-下单状态-下单的商品是否存在-用户是否合法-购买数量是否正确-此处的校验不能省略
        ItemInfo itemInfo = orderManager.verifyOrderStatus(param.getItemId(),
                param.getUserId(), param.getAmount(), param.getPromoId());
        //落单减库存/支付减库存
        orderManager.decreaseStock(param.getItemId(), param.getAmount());
        //目前使用fixThreadPool是有问题的。当20个核心线程被阻塞时，队列被无限制的填充。导致OOM。
        //本地队列泄洪，每次让20个执行，其它在队列中等待。为了防止其它任务抢锁，浪费资源
        //如果在分布式情况下可以使用分布式队列。但会产生网络消耗。
        //企业级应用中，采用分布式队列，当分布式队列延迟高时，切换到本地队列。
        Future<OrderModel> future = executorService.submit(new Callable<OrderModel>() {
            @Override
            public OrderModel call() throws Exception {
                return orderManager.createOrderInTransaction(param, itemInfo);
            }
        });
        OrderModel orderModel = null;
        try {
            orderModel = future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
        } catch (ExecutionException e) {
            e.printStackTrace();
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
        }
        return convertFromOrderModel(orderModel);
    }

    @Override
    public void savaOrderLog(OrderLogDo orderLogDo) {
        orderManager.saveOrderLog(orderLogDo);
    }

    public OrderInfo convertFromOrderModel(OrderModel orderModel) {
        if (orderModel == null) {
            return null;
        }
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderModel, orderInfo);
        return orderInfo;
    }
}
